#####################################################################
#                                                                   #
#  THIS IS A SOURCE CODE FILE FROM A PROGRAM TO INTERACT WITH THE   #
# LBRY PROTOCOL ( lbry.com ). IT WILL USE THE LBRY SDK ( lbrynet )  #
# FROM THEIR REPOSITORY ( https://github.com/lbryio/lbry-sdk )      #
# WHICH I GONNA PRESENT TO YOU AS A BINARY. SINCE I DID NOT DEVELOP #
# IT AND I'M LAZY TO INTEGRATE IN A MORE SMART WAY. THE SOURCE CODE #
# OF THE SDK IS AVAILABLE IN THE REPOSITORY MENTIONED ABOVE.        #
#                                                                   #
#      ALL THE CODE IN THIS REPOSITORY INCLUDING THIS FILE IS       #
# (C) J.Y.Amihud and Other Contributors 2021. EXCEPT THE LBRY SDK.  #
# YOU CAN USE THIS FILE AND ANY OTHER FILE IN THIS REPOSITORY UNDER #
# THE TERMS OF GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER    #
# VERSION. TO FIND THE FULL TEXT OF THE LICENSE GO TO THE GNU.ORG   #
# WEBSITE AT ( https://www.gnu.org/licenses/gpl-3.0.html ).         #
#                                                                   #
# THE LBRY SDK IS UNFORTUNATELY UNDER THE MIT LICENSE. IF YOU ARE   #
# NOT INTENDING TO USE MY CODE AND JUST THE SDK. YOU CAN FIND IT ON #
# THEIR OFFICIAL REPOSITORY ABOVE. THEIR LICENSE CHOICE DOES NOT    #
# SPREAD ONTO THIS PROJECT. DON'T GET A FALSE ASSUMPTION THAT SINCE #
# THEY USE A PUSH-OVER LICENSE, I GONNA DO THE SAME. I'M NOT.       #
#                                                                   #
# THE LICENSE CHOSEN FOR THIS PROJECT WILL PROTECT THE 4 ESSENTIAL  #
# FREEDOMS OF THE USER FURTHER, BY NOT ALLOWING ANY WHO TO CHANGE   #
# THE LICENSE AT WILL. SO NO PROPRIETARY SOFTWARE DEVELOPER COULD   #
# TAKE THIS CODE AND MAKE THEIR USER-SUBJUGATING SOFTWARE FROM IT.  #
#                                                                   #
#####################################################################

# This is the seach mechanism

import threading
from flbry import ui
from flbry import fetch
from gi.repository import Gtk
from gi.repository import GLib

def find(win, arg="", channel_ids=[], page=1, r={}, flowbox=False):
    

    # This is very dumb. I did a mess with the inputs of this command
    # now I'm trying to add a new feature, without rewriting a whole
    # lot. And it requires form me to give it a weird kind of thing.
    sr = r.copy()
    
    if "method" in r:
        method = r["method"]
        del r["method"]
        c = {}
        c["page"] = page
    else:
        method = "claim_search"

 
        c = {}
        c["text"] = arg
    
        c["channel_ids"] = channel_ids
        c["page"] = page
    
    

        c["order_by"] = "creation_height" #{"order_by":"release_time"}
        c["remove_duplicates"] = True

        # TAGS TO EXCLUDE ( E.G. 'mature', 'porn' )

        tags = win.settings["filter_tags"] # It's set in the settings
        c["not_tags"] = tags

    # Any overwrites ?
    
    for i in r:
        if r[i]:
            c[i] = r[i]
        elif c[i]:
            del c[i]
    
    channel_load = True
    if len(channel_ids) == 1:
        channel_load = False
    out = fetch.lbrynet(method, c)

    
    same = [win, arg, channel_ids, page+1, sr]

    if flowbox:
        def add_to_flowbox(flowbox, out):
            for i in out["items"]:
                flowbox.add(ui.search_item(win, i, channel_load))
            if len(out["items"]) > 1:
                lastthing(win, same, flowbox)
            flowbox.show_all()
        GLib.idle_add(add_to_flowbox, flowbox, out)
    else:
        return [win, out, channel_load, same, sr]

def render(out):

    win, out, channel_load, same, r = out

    overlay = Gtk.Overlay()
    
    scrolled = Gtk.ScrolledWindow()
    scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)

    flowbox = Gtk.FlowBox()
    flowbox.set_valign(Gtk.Align.START)
    flowbox.set_max_children_per_line(30)
    flowbox.set_selection_mode(Gtk.SelectionMode.NONE)



    scrolled.add(flowbox)
    
    box = Gtk.VBox()
    for i in out["items"]:
        flowbox.add(ui.search_item(win, i, channel_load))
    if len(out["items"]) > 1:
        lastthing(win, same, flowbox)

    overlay.add(scrolled)

    ############ SEARCH MENU ##############

    settings_menu = Gtk.Popover()
    settings_scroll = Gtk.ScrolledWindow()
    settings_menu.add(settings_scroll)
    settings_scroll.set_size_request(400,400)
    menu_box = Gtk.VBox()
    settings_scroll.add(menu_box)
    
    menu_box.pack_start(Gtk.Label("  Search Options:  "), 0,0,5)

    #### SEARCH TERMS  ###

    # name - the LBRY url of publication.

    collapsable = Gtk.Expander(label="  By Text Query:  ")
    menu_box.pack_start(collapsable, 0,0,5)

    term_box = Gtk.VBox()
    collapsable.add(term_box)

    hbox = Gtk.HBox()
    term_box.pack_start(hbox, 0,0,5)
    hbox.pack_start(Gtk.Label("  LBRY url:  "), 0,0,0)

    
    lbry_name_entry = Gtk.Entry()
    if same[-1].get("name"):
        lbry_name_entry.set_text(same[-1]["name"])
    hbox.pack_end(lbry_name_entry, 1,1,0)

    
    # text - the text in title

    hbox = Gtk.HBox()
    term_box.pack_start(hbox, 0,0,5)
    hbox.pack_start(Gtk.Label("  Search by Text:  "), 0,0,0)
    
    text_entry = Gtk.Entry()
    if same[1]:
        text_entry.set_text(same[1])
    elif same[-1].get("text"):
        text_entry.set_text(same[-1]["text"])
    hbox.pack_end(text_entry, 1,1,0)
    
    # claim_id - search partial of full claim_id

    collapsable = Gtk.Expander(label="  By Claim ID:  ")
    menu_box.pack_start(collapsable, 0,0,5)

    term_box = Gtk.VBox()
    collapsable.add(term_box)

    hbox = Gtk.HBox()
    term_box.pack_start(hbox, 0,0,5)
    hbox.pack_start(Gtk.Label("  Claim ID:  "), 0,0,0)
    
    claim_id_entry = Gtk.Entry()
    if same[-1].get("claim_id"):
        claim_id_entry.set_text(same[-1]["claim_id"])
    hbox.pack_start(claim_id_entry, 1,1,0)

    # claim_ids - search partial of full claim_id

    term_box.pack_start(Gtk.Label("  Many Full Claim IDs  "), 0,0,5)

    claim_ids_list = []
    if same[-1].get("claim_ids"):
        claim_ids_list = same[-1].get("claim_ids")
    
    claim_ids_editor = ui.tags_editor(win, claim_ids_list)
    term_box.pack_start(claim_ids_editor, 0,0,0)

    ## TAGS ##

    collapsable = Gtk.Expander(label="  Tags:  ")
    menu_box.pack_start(collapsable, 0,0,5)

    term_box = Gtk.VBox()
    collapsable.add(term_box)

    term_box.pack_start(Gtk.Label("  Any of:  "), 0,0,5)

    any_tags_list = []
    if same[-1].get("any_tags"):
        any_tags_list = same[-1].get("any_tags")
    
    tags_editor = ui.tags_editor(win, any_tags_list)
    term_box.pack_start(tags_editor, 0,0,0)

    term_box.pack_start(Gtk.Label("  All of:  "), 0,0,5)

    all_tags_list = []
    if same[-1].get("all_tags"):
        all_tags_list = same[-1].get("all_tags")
    
    tags_editor = ui.tags_editor(win, all_tags_list)
    term_box.pack_start(tags_editor, 0,0,0)

    term_box.pack_start(Gtk.Label("  Excluding:  "), 0,0,5)

    not_tags_list = []
    if same[-1].get("not_tags"):
        not_tags_list = same[-1].get("not_tags")
        
    
    tags_editor = ui.tags_editor(win, not_tags_list)
    term_box.pack_start(tags_editor, 0,0,0)

    ## CHANNELS ##

    collapsable = Gtk.Expander(label="  Channels:  ")
    menu_box.pack_start(collapsable, 0,0,5)

    term_box = Gtk.VBox()
    collapsable.add(term_box)
    
    hbox = Gtk.HBox()
    term_box.pack_start(hbox, 0,0,5)
    hbox.pack_start(Gtk.Label("  Channel Name:  "), 0,0,0)
    
    channel_entry = Gtk.Entry()
    if same[-1].get("channel"):
        channel_entry.set_text(same[-1]["channel"])
    hbox.pack_start(channel_entry, 1,1,0)    
    
    term_box.pack_start(Gtk.Label("  Anything from (channel's claim IDs):  "), 0,0,5)
    
    any_channels_list = []
    if same[2]:
        any_channels_list = same[2]
    elif same[-1].get("channel_ids"):
        any_channels_list = same[-1]["channel_ids"]
    
    tags_editor = ui.tags_editor(win, any_channels_list)
    term_box.pack_start(tags_editor, 0,0,0)

    term_box.pack_start(Gtk.Label("   Excluding (channel's claim IDs):  "), 0,0,5)

    not_channels_list = []
    if same[-1].get("not_channel_ids"):
        not_channels_list = same[-1]["not_channel_ids"]
        
    tags_editor = ui.tags_editor(win, not_channels_list)
    term_box.pack_start(tags_editor, 0,0,0)
    
    ## ORDER BY ##

    collapsable = Gtk.Expander(label="  Order:  ")
    menu_box.pack_start(collapsable, 0,0,5)

    term_box = Gtk.VBox()
    collapsable.add(term_box)

    order_by_rules = {
        "Name":"name",
        "Release Time":"release_time",
        "Trending":"trending_mixed",
        "Trending (Global)":"trending_global",
        "Trending (Group)":"trending_group",
        "Trending (Local)":"trending_local",
        "Bid":"amount",
        "Support":"support_amount",
        "Support plus Bid":"effective_amount",
        "Block Height":"height",
        "Start Block":"activation_height"
        
    }
    
    order_by = Gtk.ComboBoxText()
    

    was_set = False
    for n, i in enumerate(order_by_rules):
        order_by.append_text(i)
        if order_by_rules[i] in str(same[-1].get("order_by")):
            order_by.set_active(n)
            was_set = True

    if not was_set:
        order_by.set_active(1)

    term_box.pack_start(order_by, 0,0,5)
    
    hbox = Gtk.HBox()
    term_box.pack_start(hbox, 0,0,5)
    hbox.pack_start(Gtk.Label("  Reverse:  "), 0,0,0)
    order_reverse = Gtk.Switch()
    if "^" in str(same[-1].get("order_by")):
        order_reverse.set_active(True)
    hbox.pack_end(order_reverse, 0,0,0)
    
    ## ORDER BY ##

    collapsable = Gtk.Expander(label="  Type:  ")
    menu_box.pack_start(collapsable, 0,0,5)

    term_box = Gtk.VBox()
    collapsable.add(term_box)

    claim_types = {
        "Every Claim Type":"",
        "Channel":"channel",
        "File":"stream",
        "Re-Post":"repost",
        "Collection":"collection"
    }
    claim_type = Gtk.ComboBoxText()
    was_set = False
    for n, i in enumerate(claim_types):
        claim_type.append_text(i)
        if claim_types[i] in str(same[-1].get("claim_type")):
           claim_type.set_active(n)
           was_set = True

    if not was_set:
        claim_type.set_active(0)

    term_box.pack_start(claim_type, 0,0,5)

    ## FILETYPES ##
    
    term_box.pack_start(Gtk.Label("  Filetypes:  "), 0,0,5)
    
    stream_types = []
    if same[-1].get("stream_types"):
        stream_types = same[-1]["stream_types"]
        
    stream_types_editor = ui.tags_editor(win, stream_types)
    stream_types_editor.set_tooltip_text("Types of a file. For example: video, audio, image, document, binary.")

    term_box.pack_start(stream_types_editor, 0,0,5)

    ## MIME-TYPES ##
    
    term_box.pack_start(Gtk.Label("  Mime-Types:  "), 0,0,5)
    
    media_types = []
    if same[-1].get("media_types"):
        media_types = same[-1]["media_types"]
        
    media_types_editor = ui.tags_editor(win, media_types)
    media_types_editor.set_tooltip_text("Precise Formats. For example: image/png, audio/ogg, application/x-ext-blend")
    
    term_box.pack_start(media_types_editor, 0,0,5)
    

    
    ####### !!! RE _ SEARCH !!! #######

    def do_re_search(w):

        request = {}

        ## TEXT ##
        if lbry_name_entry.get_text():
            request["name"] = lbry_name_entry.get_text()
        if text_entry.get_text():
            request["text"] = text_entry.get_text()
        ## CLAIM ID ##
        if claim_id_entry.get_text():
            request["claim_id"] = claim_id_entry.get_text()
        if claim_ids_list:
            request["claim_ids"] = claim_ids_list
        ## TAGS ##
        if any_tags_list:
            request["any_tags"] = any_tags_list
        if all_tags_list:
            request["all_tags"] = all_tags_list
        if any_tags_list:
            request["not_tags"] = not_tags_list
        ## CHANNELS ##
        if channel_entry.get_text():
            request["channel"] = channel_entry.get_text()
        if any_channels_list:
            request["channel_ids"] = any_channels_list
        if not_channels_list:
            request["not_channel_ids"] = not_channels_list
        ## Order by ##
        prefix = ""
        if order_reverse.get_active():
            prefix = "^"
        request["order_by"] = prefix+list(order_by_rules.values())[order_by.get_active()]

        ## CLAIM TYPE ##
        if list(claim_types.values())[claim_type.get_active()]:
            request["claim_type"] = list(claim_types.values())[claim_type.get_active()]
            # Some logic related to what the user will percieve
            if request["channel_ids"] and not request.get("claim_ids") and request["claim_type"] == "channel":
                request["claim_ids"] = request["channel_ids"]
                request["channel_ids"] = []
        ## STREAM TYPE ##
        if stream_types:
            request["stream_types"] = stream_types
        ## MIME TYPES ##
        if media_types:
            request["media_types"] = media_types

            
        box = overlay.get_parent()
        overlay.destroy()
        resolve = ui.load(win, find, render, win, "", [], 1, request)
        box.pack_start(resolve, 1,1,0)
        box.show_all()
        
    re_search = Gtk.Button()
    re_search.connect("clicked", do_re_search)
    re_search.set_relief(Gtk.ReliefStyle.NONE)
    b = Gtk.HBox()
    b.pack_start(ui.icon(win, "system-search"),0,0,0)
    b.pack_start(Gtk.Label("  Re-Search  "),0,0,0)
    re_search.add(b)

    
    menu_box.pack_end(re_search, 0,0,0)

    


    
    settings_scroll.show_all()
    
    b = Gtk.MenuButton(popover=settings_menu)
    b.add(ui.icon(win, "preferences-system"))
    b.set_halign(Gtk.Align.END)
    b.set_valign(Gtk.Align.END)
    overlay.add_overlay(b)
        
    return overlay

def lastthing(win, same, flowbox):
    # THis is a hack. I use a spinner to get a draw event
    # when the user scrolls far enough, it will execute it
    # to load more stuff
    spinner_more = ui.icon(win, "loading", "gif")
    flowbox.add(spinner_more)
    def draw_event(w, e):
        
        print("EVENT MOTHERFUCKER")
        w.destroy()
        same.append(flowbox)

        print("SAME:", same)

        load_thread = threading.Thread(target=find, args=[*same])
        load_thread.setDaemon(True)
        load_thread.start()

    spinner_more.connect("draw", draw_event)
        
    
